using System;
using System.Collections.Generic;
using System.Reflection;

namespace YouVisio.Silverlight.TextDiff
{
    public class TextDiffFactory : ITextDiffFactory
    {
        #region static helpers
        public static ITextDiffFactory Create(string typeFullName)
        {
            if (String.IsNullOrEmpty(typeFullName)) return new TextDiffFactory();
            var type = GetType(typeFullName);
            if (type == null)
            {
                throw new ArgumentException("Cannot find type " + typeFullName);
            }
            if (!typeof(ITextDiffFactory).IsAssignableFrom(type))
            {
                throw new ArgumentException("Type " + typeFullName + " cannot be assigned to ITextDiffFactory");
            }
            return Activator.CreateInstance(type) as ITextDiffFactory;
        }
        private static readonly object _synchObject = new object();
        private static readonly Dictionary<string, Assembly> _assemblyCache = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
        public static Type GetType(string typeFullName)
        {
            var arr = typeFullName.Split(',');
            if (arr.Length == 1) return Type.GetType(typeFullName);
            var namespaceAndClass = arr[0].Trim();
            var assemblyName = EnsureSuffix(arr[1].Trim());
            Assembly assembly;
            if (!_assemblyCache.TryGetValue(assemblyName, out assembly))
            {
                lock (_synchObject)
                {
                    if (!_assemblyCache.TryGetValue(assemblyName, out assembly))
                    {
                        foreach (var part in System.Windows.Deployment.Current.Parts)
                        {
                            var assemblyWithoutDll = EnsureSuffix(part.Source);
                            if (assemblyName.Equals(assemblyWithoutDll, StringComparison.OrdinalIgnoreCase))
                            {
                                assembly =
                                    new System.Windows.AssemblyPart().Load(
                                        System.Windows.Application.GetResourceStream(new Uri(part.Source,
                                                                                             UriKind.Relative)).Stream);
                                _assemblyCache.Add(assemblyWithoutDll, assembly);
                                break;
                            }
                        }
                    }
                }
            }
            return assembly.GetType(namespaceAndClass);
        }
        private static string EnsureSuffix(string assemblyName)
        {
            if (String.IsNullOrEmpty(assemblyName)) return assemblyName;
            assemblyName = assemblyName.Trim();
            return assemblyName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)
                            ? assemblyName.Substring(0, assemblyName.Length - 4)
                            : assemblyName;
        }
        #endregion

        public virtual void Initialize(ITemplateSource templateSource, ISettingsSource settingsSource)
        {
            TemplateSource = templateSource;
            SettingsSource = settingsSource;
            DiffAnalyser = new DiffAnalyser();
            TextPartFinder = new TextPartFinder(this, DiffAnalyser);
            InputAccessor = new InputAccessor(SettingsSource);
            OutputManager = new OutputManager(SettingsSource);
            TextDiffViewerUI = new TextDiffViewerUI(this, TemplateSource, SettingsSource, InputAccessor, TextPartFinder, OutputManager);
        }

        public virtual IPartsRetriever<int[], int> GetNewInt32PartsRetriever()
        {
            return new Int32Retriever();
        }
        public virtual IWordsRetriever GetNewWordsRetriever()
        {
            return new WordsRetriever();
        }
        public virtual IDiffAnalyser DiffAnalyser { get; private set; }
        public virtual ITemplateSource TemplateSource { get; private set; }
        public virtual ISettingsSource SettingsSource { get; private set; }
        public virtual ITextDiffViewerUI TextDiffViewerUI { get; private set; }
        public virtual ITextPartFinder TextPartFinder { get; private set; }
        public virtual IInputAccessor InputAccessor { get; private set; }
        public virtual IOutputManager OutputManager { get; private set; }
    }
}